﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace DeSleeper.Client.Security
{
	internal class CredentialsPrompt
	{
		private const int MAX_USER_NAME = 100;
		private const int MAX_PASSWORD = 100;
		private const int MAX_DOMAIN = 100;

		public static CredUIReturnCodes PromptForCredentials(
				 ref CREDUI_INFO creditUI,
				 String targetName,
				 int netError,
				 ref string userName,
				 ref string password,
				 ref bool save,
				 CREDUI_FLAGS flags)
		{
			var user = new StringBuilder(MAX_USER_NAME);
			var pwd = new StringBuilder(MAX_PASSWORD);
			var saveCredentials = Convert.ToInt32(save);
			creditUI.cbSize = Marshal.SizeOf(creditUI);

			CredUIReturnCodes result = CredUIPromptForCredentials(ref creditUI, targetName,
				IntPtr.Zero, netError,
				user, MAX_USER_NAME,
				pwd, MAX_PASSWORD,
				ref saveCredentials, flags);

			save = Convert.ToBoolean(saveCredentials);
			userName = user.ToString();
			password = pwd.ToString();
			return result;
		}

		public static CredUIReturnCodes ParseUserName(string userName,
			ref string userPart, ref string domainPart)
		{
			var user = new StringBuilder(MAX_USER_NAME);
			var domain = new StringBuilder(MAX_DOMAIN);
			CredUIReturnCodes result = CredUIParseUserName(userName,
														   user, MAX_USER_NAME,
														   domain, MAX_DOMAIN);
			userPart = user.ToString();
			domainPart = domain.ToString();
			return result;
		}

		public static CredUIReturnCodes ConfirmCredentials(string target,
				 bool confirm)
		{
			return CredUIConfirmCredentials(target, confirm);
		}

		/// <exception cref="InvalidOperationException"><c>InvalidOperationException</c>.</exception>
		public static void DeleteCredential(string target, CRED_TYPE credentialType)
		{
			if (!CredDelete(target, credentialType, 0))
				throw new InvalidOperationException(string.Format("Failed to delete credential [{0}] from credential manager.  Error code: {1}",
					target, Marshal.GetLastWin32Error()));
		}


		[DllImport("credui", EntryPoint = "CredUIPromptForCredentialsW", CharSet = CharSet.Unicode)]
		private static extern CredUIReturnCodes CredUIPromptForCredentials(
			ref CREDUI_INFO creditUR,
			string targetName,
			IntPtr reserved1,
			int iError,
			StringBuilder userName,
			int maxUserName,
			StringBuilder password,
			int maxPassword,
			ref int iSave,
			CREDUI_FLAGS flags);

		[DllImport("credui", EntryPoint = "CredUIParseUserNameW", CharSet = CharSet.Unicode)]
		private static extern CredUIReturnCodes CredUIParseUserName(
			string userName,
			StringBuilder user,
			int userMaxChars,
			StringBuilder domain,
			int domainMaxChars);

		[DllImport("credui", EntryPoint = "CredUIConfirmCredentialsW", CharSet = CharSet.Unicode)]
		private static extern CredUIReturnCodes CredUIConfirmCredentials(string targetName, bool confirm);

		[DllImport("AdvApi32", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
		private static extern bool CredDelete(
			string targetName,
			CRED_TYPE Type,
			int flags /* Resrved, must be zero*/);

		[DllImport("Gdi32", CharSet = CharSet.Auto)]
		private static extern bool DeleteObject(IntPtr hObject);

	}

	internal enum CRED_TYPE
	{
		GENERIC = 1,
		DOMAIN_PASSWORD = 2,
		DOMAIN_CERTIFICATE = 3,
		DOMAIN_VISIBLE_PASSWORD = 4
	}

	[Flags]
	internal enum CREDUI_FLAGS
	{
		INCORRECT_PASSWORD = 0x1,
		DO_NOT_PERSIST = 0x2,
		REQUEST_ADMINISTRATOR = 0x4,
		EXCLUDE_CERTIFICATES = 0x8,
		REQUIRE_CERTIFICATE = 0x10,
		SHOW_SAVE_CHECK_BOX = 0x40,
		ALWAYS_SHOW_UI = 0x80,
		REQUIRE_SMARTCARD = 0x100,
		PASSWORD_ONLY_OK = 0x200,
		VALIDATE_USERNAME = 0x400,
		COMPLETE_USERNAME = 0x800,
		PERSIST = 0x1000,
		SERVER_CREDENTIAL = 0x4000,
		EXPECT_CONFIRMATION = 0x20000,
		GENERIC_CREDENTIALS = 0x40000,
		USERNAME_TARGET_CREDENTIALS = 0x80000,
		KEEP_USERNAME = 0x100000
	}

	internal enum CredUIReturnCodes
	{
		NO_ERROR = 0,
		ERROR_CANCELLED = 1223,
		ERROR_NO_SUCH_LOGON_SESSION = 1312,
		ERROR_NOT_FOUND = 1168,
		ERROR_INVALID_ACCOUNT_NAME = 1315,
		ERROR_INSUFFICIENT_BUFFER = 122,
		ERROR_INVALID_PARAMETER = 87,
		ERROR_INVALID_FLAGS = 1004
	}

	internal struct CREDUI_INFO
	{
		public int cbSize;
		public IntPtr hwndParent;
		[MarshalAs(UnmanagedType.LPWStr)]
		public string pszMessageText;
		[MarshalAs(UnmanagedType.LPWStr)]
		public string pszCaptionText;
		public IntPtr hbmBanner;
	}
}
